/**
 * HTML5 WebViewer Library
 * --------------------------------------------------------------------------------
 * version 0.9.20131217
 * author: Martin Metal
 *
 * This library can dynamically build HTML5 markup in a browser and display
 * a document. The rendering and format conversion functionality remains
 * centralized in the WebViewer core. New UI implements functions that
 * minimizes the number of rendering requests sent to the WebViewer core
 * in standard mode (zooming and annotating).
 *
 * ---------------------------------------------------------------------
 * This version uses the annotation rendering rendering facility provided
 * by the WebViewer. The annotations are fixed part of the image already
 * No third party library required and annotations are part of the magnifier
 * ---------------------------------------------------------------------
 *
 * WARRANTY: THE CODE IS IN EXPERIMENTAL STAGE. IS MUST NOT BE USED
 *           IN PRODUCTIVE CODE IN ANY FORM. 
 */

/*
 * This code uses segment from Wadda library, published by Zohaib Sibt-e-Hassan under the MIT license.
 * http://blog.insicdesigns.com/2010/04/introducing-wadda-the-image-zoomer-using-canvas/
 */

/*
The MIT License

Copyright (c) 2010 Zohaib Sibt-e-Hassan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// Defining the globals, prototype overrides.
//<<<
Function.prototype.scope = function(){
	var argsOuter = Array.prototype.slice.call(arguments);
	var func = this;
	var scope = argsOuter[0];
	
	if(argsOuter.length > 1 && argsOuter[1] && !argsOuter[1].length)
		argsOuter = argsOuter.slice(1);
	else if( argsOuter[1] && argsOuter[1].length > 1)
		argsOuter = argsOuter[1];
	else
		argsOuter = [];

	var ret = function (){
		var args = Array.prototype.slice.call(arguments);
		args = argsOuter.concat(args);
		func.apply(scope, args);
	};
	return ret;
};

var listenOn = null;

var deafOn = function(el, name, cb){
	if(!el)
		return false;
	if(cb){
		if( el.detachEvent )
			return el.detachEvent('on'+name, cb);
		else if( el.removeEventListener )
			return el.removeEventListener(name, cb, false);
		return false;
	}
};

(function(){

	var evDispatcher = function(cback, e){
		if(!e) e=window.event;
		cback.scope(this)(e);
	};

	listenOn = function(el, name, cb){
		var ret;
		if(!el)
			return ret;
		if(el.attachEvent)
			ret = el.attachEvent("on"+name, evDispatcher.scope(el, cb.scope(el) ) );
		else if(el.addEventListener)
			ret = el.addEventListener(name, evDispatcher.scope(el, cb.scope(el) ), false);
			
		return ret;
	};
})();
//>>>

// Define the application namespace.
var opentext = {}; 

// Localization object
opentext.localization = (function() {
	return {
		"MSGID000": "Test Message<br/>This is the testing message only, for localization purposes",
		"MSGID001": "Cross-Domain scripting is not allowed<br/>Application cannot retrieve document properties",
		"MSGID002": "Illegal response from server<br/>The response from the server cannot be parsed.",
		"MSGID003":	"Cannot retrieve document properties<br/>The application cannot retrieve document properties",
		"MSGID004": "Cannot create HTML5 element &lt;CANVAS&gt;<br/>Your browser does not seem to support HTML5 elements",
		"MSGID005": "Container element for image data is not defined.",
		"MSGID006": "Image container for the magnifier was created but contains no valid image data.",
		"MSGID007": "The magnifier data are not loaded correctly. Image information missing.",
		"MSGID008": "Cannot create viewer object, invalid archivelink data",
		"MSGID009": "Cannot place viewer object in the document. Given container ID does not exist.",
		"MSGID010": "The canvas does not exist. Cannot place content on non-existent canvas.",
		"MSGID011": "You are already on the first/last page of the document",
		"MSGID012": "Cannot display about dialog.",
		"MSGID013": "Unable to open document",
		"MSGID014": "Either the file format is not supported or <br/>server problems occurred when opening the document.",
		"MSGID015": "Close"
	};
}());

// This painting object works like a drawing pencil which tracks the mouse movements.
// The mouse movement is stored in a contained inside the object itself. The data
// gets removed by each initilization cycle and by each use.
opentext.FreehandTool = function( cnv, ctx ) {
	var tool = this;
	tool.conf = {
		started: false,
		canvas: cnv,
		context: ctx,
		anno: [], // contained for annotation. Technically it is array of arrays. 
		anno: {
			counter: 0 // count how many annotations were put on the drawing.
		}
	};
	return {
		conf: this.conf,

		redrawMyAnnotation: function() {
			var tool = this;
			var an;
			var el;
			if( typeof tool.conf.anno == "undefined" )
				return;
	
			tool.conf.context.globalAlpha= "1.0";
			tool.conf.context.strokeStyle = "#000000";
	
			while( an = tool.conf.anno.shift() ) {
				el = an.shift();
				tool.conf.context.beginPath();
				tool.conf.context.moveTo(el.x, el.y);
				while( el = an.shift() ) {
					tool.conf.context.lineTo(el.x,el.y);
					tool.conf.context.stroke();
				}
				tool.conf.context.closePath();
			}
			tool.conf.anno = [];
			tool.conf.anno.counter = 0;
		},

		mousedown: function (ev) {
			var tool = this;
			if( true == ev.ctrlKey ) { // pressing CTRL and clicking into canvas will clear it.
	  			console.log("canvas deleted!");
				tool.conf.context.clearRect(0, 0, tool.conf.canvas.width, tool.conf.canvas.height );
				return;
			} 
			if( true == ev.shiftKey ) { // pressing SHIFT and clicking into cnavas will restore the recorded annotations.
				console.log("captured annotation redrawn");
				tool.redrawMyAnnotation();
				return;
			}
			// this is the drawing section. When clicking in the canvas the drawing will be initiated. 
			// The drawing itself is done once in 100ms, so mot every tiny movement of the mouse 
			// translates into freehand element.
			tool.conf.context.globalAlpha= "1.0";
			tool.conf.context.strokeStyle = "#000000";
			tool.conf.context.beginPath();
			tool.conf.context.moveTo(ev._x, ev._y);
			tool.conf.lastX = ev._x;
			tool.conf.lastY = ev._y
			tool.conf.intervalID = setInterval( "opentext.tools.data.pencil.doLine()", 100 );
			tool.conf.started = true;
			tool.conf.anno[tool.conf.anno.counter] = [];
		},

		mousemove: function (ev) {
			var tool = this;
			if (tool.conf.started) {
				tool.conf.lastX = ev._x;
				tool.conf.lastY = ev._y
			}
		},

		doLine: function() {
			var tool = this;
			console.log("painting");
			tool.conf.context.lineTo(tool.conf.lastX, tool.conf.lastY);
			tool.conf.context.stroke();
			tool.conf.anno[tool.conf.anno.counter].push( { "x":tool.conf.lastX, "y":tool.conf.lastY } ); 
		},
		
		// This is called when you release the mouse button.
		// stop recording the annotation coordinates and stop drawing.
		// increase the annotation counter so next time a new annotation
		// gets recorded.
		mouseup: function (ev) {
			var tool = this;
			if (tool.conf.started) {
				tool.conf.context.closePath();
				tool.mousemove(ev);
				tool.conf.started = false;
				clearInterval(tool.conf.intervalID);
				tool.conf.anno.counter++;
			}
	  }
	}
};

// This painting object works like a drawing pencil which tracks the mouse movements.
// The mouse movement is stored in a contained inside the object itself. The data
// gets removed by each initilization cycle and by each use.
function	FreehandTool ( cnv, ctx ) {
		  var tool = this;
		  this.started = false;
        this.canvas = cnv;
        this.context = ctx;
        this.anno = []; // contained for annotation. Technically it is array of arrays. 
		  this.anno.counter = 0; // count how many annotations were put on the drawing.
		
        // This method restored the saved annotation and put them back in the drawing. Attantion, 
        // this method is destructive and you can read the local container just one. I is a poor
        // man example using push() and shift() array access control, which make it a LIFO queue.
        this.redrawMyAnnotation = function() {
	         var an;
	         var el;
	         if( typeof tool.anno == "undefined" )
		         return;

	         tool.context.globalAlpha= "1.0";
	         tool.context.strokeStyle = "#000000";

         	while( an = tool.anno.shift() ) {
	         	el = an.shift();
		         tool.context.beginPath();
		         tool.context.moveTo(el.x, el.y);
					while( el = an.shift() ) {
						tool.context.lineTo(el.x,el.y);
						tool.context.stroke();
					}
					tool.context.closePath();
				}
				tool.anno = [];
				tool.anno.counter = 0;
			};

		  // This is called when you start holding down the mouse button.
		  // This starts the pencil drawing.
        // For simplisity reasons, this function also impletemts two extra 
        // operation:
        // 1) clean the drawing canvas
        // 2) place the recored annotation on the canvas.
		  this.mousedown = function (ev) {
            if( true == ev.ctrlKey ) { // pressing CTRL and clicking into canvas will clear it.
               console.log("canvas deleted!");
               tool.context.clearRect(0, 0, tool.canvas.width, tool.canvas.height );
               return;
            } 
            if( true == ev.shiftKey ) { // pressing SHIFT and clicking into cnavas will restore the recorded annotations.
               console.log("captured annotation redrawn");
               tool.redrawMyAnnotation();
               return;
            }
            // this is the drawing section. When clicking in the canvas the drawing will be initiated. 
            // The drawing itself is done once in 100ms, so mot every tiny movement of the mouse 
            // translates into freehand element.
	         tool.context.globalAlpha= "1.0";
	         tool.context.strokeStyle = "#000000";
	         tool.context.beginPath();
	         tool.context.moveTo(ev._x, ev._y);
            tool.lastX = ev._x;
            tool.lastY = ev._y
            tool.intervalID = setInterval( "opentext.tools.data.pencil.doLine()", 100 );
	         tool.started = true;
				tool.anno[tool.anno.counter] = [];
		  };
		
		  // This function is called every time you move the mouse. Obviously, it only 
		  // draws if the tool.started state is set to true (when you are holding down 
		  // the mouse button).
		  this.mousemove = function (ev) {
		    if (tool.started) {
            tool.lastX = ev._x;
            tool.lastY = ev._y
		    }
		  };

        // This is the drawing method, is not called each time the mousemove event fires,
        // just every 100 ms. Otherwise the datastream generated for freehand annotation
        // would be flooding the buffer ...
        this.doLine = function() {
            console.log("painting");
		      tool.context.lineTo(tool.lastX, tool.lastY);
		      tool.context.stroke();
            tool.anno[tool.anno.counter].push( { "x":tool.lastX, "y":tool.lastY } ); 
        }
		
		  // This is called when you release the mouse button.
        // stop recording the annotation coordinates and stop drawing.
        // increase the annotation counter so next time a new annotation
        // gets recorded.
		  this.mouseup = function (ev) {
		    if (tool.started) {
	         tool.context.closePath();
		      tool.mousemove(ev);
		      tool.started = false;
	         clearInterval(tool.intervalID);
				tool.anno.counter++;
		    }
		  };
	}

// The general-purpose event handler. This function just determines the mouse 
// position relative to the canvas element. It is the key fucntion for capturing
// the correct coordinates for the paited object. The event is bound to the canvas
// itself, which is not rotated. I rotate the div contained of the canvas itself!
// Additionally, the key is to use CSS pixels to read the mouse position. That will
// give you always the right coordinates regardless what CSS transformation is applied.
// Attention, apply CSS transformation only, everything else is trasforming the data, 
// which is what is definitively dont want to get.
// The only operation that must happen to logged coordinates is the scalling based
// on the scalling used to generate the image. You can do this operation before
// uploading to archive server, you don't need to to place the annotation.
function ev_canvas (ev) {
	if (! ev.hasOwnProperty('offsetX')) {
		var curleft = curtop = 0;
		if (ev.offsetParent) {
			var obj=ev;
			do {
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			} while (obj = obj.offsetParent);
		}
 		ev.offsetX=ev.layerX-curleft;
		ev.offsetY=ev.layerY-curtop;
	}
	
	if (ev.offsetX || ev.offsetX == 0) { // Opera || Maxthron || Chrome || IE10 are OK. Mozila not tested yet.
		ev._x = ev.offsetX;
		ev._y = (ev.offsetY );
	} else {
		console.err("Cannot determine the mouse ccordinates");
		return;
	}
		
	// Call the event handler of the pencil tool to draw.
	var func = opentext.tools.data.pencil[ev.type];
	if( typeof func == 'function' ) {
		func(ev);
	} else {
		console.error(" reflection did not work.");
	}
}  

// Data Beans - static object keeping the configuration parameters for
// across the application namespace. As a static object the initial
// structure is set up at startup and can keep the data without
// calling the constructor.
//
// This strategy defines data bean inside the namepsace opentext.
// The bean can store arbitrary data and give them back to callee.
// The immediate function pattern is used to define the Bean
// Container.
opentext.beans = {};
opentext.beans.webviewer = (function() {
//<<<
	var me = this;
	me.conf = {};	
	return {
		conf: this.conf,
		set: function( pars ) {
//			console.log('opentext.beans.set() called');
			opentext.tools.merge( this.conf, pars );
//			for (var i in me.conf)
//				console.log( '	' + i + ' = ' + me.conf[i] + " is of type " + typeof me.conf[i]);
		},
		get: function( n ) {
//			console.log('opentext.beans.get(' + n + ') enter');
			if( typeof conf[n] === 'undefined' ) {
				console.warn( 'opentext.beans.webviewer Bean does not contain property: ' + n ); 
				return 'undefined';
			}
//			console.log('opentext.beans.get(' + n + ') returning ' + conf[n] );
			return conf[n];	
		}
	};
}());
//>>>

// Common tools inside the application namespace.
// Used pattern - immediate function - makes sure that the
// opentext.tools is a pointer to object having an public API.
// The pattern produces a static object, accessible across the
// application namespace.
opentext.tools = (function() {
//<<<
	var me = this;
	var bean = opentext.beans.webviewer;
	var a,a0;
	me.data = {};
	return {
		data: this.data,

		getVal: function( url, pattern ) {
			var tmp;
			console.log( 'opentext.tools.getVal() search for pattern = ' + pattern + ' in URL = ' + url );
			tmp = url.slice(url.indexOf("&") + 1);
			a = tmp.split("&");
			if( typeof a === 'object' && a.length > 0 ) {
				for( var it in a ) {
					if( typeof a[it] !== 'undefined' ) {
						a0 = a[it].split("=");
						if( a0[0] === pattern ) {
							tmp = a0[1];
							break;
						}
					}
				}
			} else {
				console.warn( 'opentext.tools.getVal(): the source is not an array. Cannot find a pattern' );
				tmp = "";
			}

			console.log( 'opentext.tools.getVal() found pattern = ' + pattern + ' = ' + tmp );
			return tmp;
		},

		encodeurl: function( str ) {
			var tmp = encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
			console.log( 'opentext.tools.encoderurl input = ' + str );
			console.log( 'opentext.tools.encoderurl result = ' + tmp );
			return tmp;
		},

		decodeHTML: function( str ) {
			var tmp = str.split("&#034;").join('"'); 
			return tmp;
		},

		merge: function(a,b) {
			console.log('opentext.tools.merge() called');
			for( var i in b )
				if(b[i] !== undefined)
					a[i] = b[i];
		},

		infoZoomFactorHide: function() {
//			var vc = document.getElementById( 'viewer_canvas' );
			var vc = document.getElementById( bean.get( 'wall' ) );
			var o = document.getElementById( 'zoom_level_info' );
			vc.removeChild( o );
		},

		infoZoomFactorShow: function( zf ) {
//			var vc = document.getElementById( 'viewer_canvas' );
			var vc = document.getElementById( bean.get( 'wall' ) );
			var nim = document.createElement('div');
			nim.setAttribute('id', 'zoom_level_info' );
			nim.setAttribute('class', 'showbox scale' );
			nim.innerHTML = " " + zf + "x";
			vc.appendChild( nim );
			setTimeout( 'opentext.tools.infoZoomFactorHide()', 1500 );
		}, 

		notificationDestroy: function() {
			var vc = document.getElementById( bean.get( 'wall' ) );
			var o = document.getElementById( 'notification_box' );
			vc.removeChild( o );
		},

		notification: function( txt, style, time ) {
			var vc = document.getElementById( bean.get( 'wall' ) );
			var nim = document.createElement('div');
			nim.setAttribute('id', 'notification_box' );
//			nim.setAttribute('class', 'loading loading_blue' );
			nim.setAttribute('class', style );
			nim.innerHTML = txt;
			vc.appendChild( nim );
			setTimeout( 'opentext.tools.notificationDestroy()', time );
		},

		getDocumentInfo: function( url ) {
			console.log( "INFO: Requesting data from server: " + url ); 
			req = new XMLHttpRequest();

			req.open( "GET", url, true );
//			req.onreadystatechange = this.processXMLRequest;
			req.onreadystatechange = this.processJSONRequest;
			req.setRequestHeader("Content-Type", "application/xml");
			req.setRequestHeader("x-customheader", "webviewer" );
			req.send();
			console.log( "XMLHttpRequest object successfully constructed" );
		},

		processJSONRequest: function() {
			var jsonDoc = null;
			var o = {};
			var wtxt;
			console.log( "Request fired for response parsing JSON" );

			if (req.readyState == 4 && req.status == 200) {
				console.log( "INFO: Request fired and response came back with status 200" );

				jsonDoc = req.responseText; 
				if( jsonDoc ) {
					wtxt = jsonDoc.replace( /.*\<json_tag\>/m, "" );
					wtxt = wtxt.replace( /\<\/json_tag\>/m, "" );
					wtxt = opentext.tools.decodeHTML( wtxt ); 
					o = JSON.parse(wtxt);
					opentext.beans.webviewer.set( o );
					opentext.tools.showDocumentProperties();
				} else {
					console.log( "ERROR: cannot retrieve the elements from JSON structure" ); }
			} else if ( req.readyState == 4 && req.status == 0 ) {
				opentext.tools.notification( opentext.localization.MSGID001, 'loading loading_red', 4000 );
			} else {
			console.log( 'XMLHttpResponse: state=' + req.readyState + ', status=' + req.status );
		  }
		},

		processXMLRequest: function() {
			var xmlDoc = null;
			var o = {};
			console.log( "INFO: Request fired and response came back with status 200" );

			if (req.readyState == 4 && req.status == 200) {
				console.log( "INFO: Request fired and response came back with status 200" );

				xmlDoc = req.responseXML;
	
				if( xmlDoc ) {
					var arr = xmlDoc.childNodes[1].childNodes;
	
					if( arr ) {
						for( var i = 0; i< arr.length; i++ ) {
							if( arr[i].nodeName.toLowerCase() == "pagecount" ) {
								o.pageCount = parseInt( arr[i].textContent, 10 );
								console.log( "INFO: retrieved pagecount = " + o.pagecount );
							}
							if( arr[i].nodeName.toLowerCase() == "hasnotes" ) {
								o.hasNotes = arr[i].textContent;
								console.log( "INFO: retrieved hasNotes = " + o.hasnotes );
							}
						}
						opentext.beans.webviewer.set( o );
					} else {
						console.log( "ERROR: cannot retrieve the elements from XML structure" );
						opentext.tools.notification( opentext.localization.MSGID002, 'loading loading_red', 4000 );
					}
				} else {
					console.log( "ERROR: cannot retrieve the elements from XML structure" );
					opentext.tools.notification( opentext.localization.MSGID002, 'loading loading_red', 4000 );
				}
			} else if ( req.readyState == 4 && req.status == 0 ) {
				opentext.tools.notification( opentext.localization.MSGID001, 'loading loading_red', 4000 );
			} else {
			console.log( 'XMLHttpResponse: state=' + req.readyState + ', status=' + req.status );
		  }
	  },
		
		loadingStart: function(me) {
			var vc = document.getElementById( bean.get( 'wall' ) );
			var nim = document.createElement('div');
			var nii = document.createElement('img');

//			nii.setAttribute( 'src', 'img/loading.gif');
			nii.setAttribute( 'src', 'http://' + bean.get('webviewer') + '/WebViewer/res/img/loading.gif');
			
			console.log( 'opentext.tools.loadingStart() called' );
			nim.setAttribute('id', 'loading_info' );
			nim.setAttribute('class', 'loading loading_white' );
			nim.appendChild( nii );
			vc.appendChild( nim );
		},

		loadingFailed: function(me) {
			var vc = document.getElementById( bean.get( 'wall' ) );
			var nim = document.createElement('div');
			console.log( 'opentext.tools.loadingStart() called' );
			nim.setAttribute('id', 'loading_info' );
			nim.setAttribute('class', 'loading loading_blue' );
			nim.innerHTML = "Document cannot be loaded";
			vc.appendChild( nim );
		},

		loadingComplete: function(me) {
			var vc = document.getElementById( bean.get( 'wall' ) );
			var o = document.getElementById( 'loading_info' );
			console.log( 'opentext.tools.loadingComplete() called' );
			vc.removeChild( o );

			var url = ( bean.get( 'info_url' ) !== 'undefined' ? bean.get( 'info_url' ) : null );
			var maxPage= (bean.get( 'pageCount' ) !== 'undefined' ? bean.get( 'pageCount' ) : 'undefined' );
			if( url !== null ) {
				if( (maxPage.match(/undefined/) !== null) || (maxPage == 0)  )
					opentext.tools.getDocumentInfo( url );
				else
					opentext.tools.showDocumentProperties();
			} else {
				console.error( 'ERROR: cannot retrieve document information' );
				opentext.tools.notification( opentext.localization.MSGID003, 'loading loading_red', 4000 );
			}
		},

		loadImage: function(src, cb){
			var img = new Image();
			img.src = src;
			listenOn(img, "load", cb.scope(img, img) );
			return img;
		},
		
		createScaledImageCanvas: function(img, zoom, orgImg){
			var tmp = document.createElement('canvas');
			tmp.setAttribute( 'id', 'zoomer' );
			var orgSz = this.getImageSize(orgImg);
			tmp.width = img.width*zoom;
			tmp.height = img.height*zoom;
			var con = tmp.getContext('2d');
	// MAME fix: virtual positioning of the canvas in the new div.
	//		con.drawImage(orgImg, 0, 0, orgSz.w, orgSz.h, 0, 0, tmp.width, tmp.height);
	//		console.log( " Drawing Canvas, source = " + orgSz.w + ":" + orgSz.h );
	//		console.log( " Drawing Canvas, destination = " + tmp.width + ":" + tmp.height );
	//		Factor 60 works for zoom factor 2x. Must be adjusted for higher factors.
	//		con.drawImage(orgImg, 0, 0, orgSz.w, orgSz.h, Math.floor(30 * zoom), Math.floor(30 * zoom), tmp.width, tmp.height );
			con.drawImage(orgImg, 0, 0, orgSz.w, orgSz.h, 0, 0, tmp.width, tmp.height );
			return tmp;
		},
		
		getBrowserDimension: function () {
			var dimension = { pageWidth: 0, pageHeight: 0, windowWidth: 0, windowHeight: 0 };

			if (window.innerHeight && window.scrollMaxY) {  
				dimension.pageWidth = document.body.scrollWidth;
				dimension.pageHeight = window.innerHeight + window.scrollMaxY;
			} 
			else if (document.body) {
				if (document.body.scrollHeight > document.body.offsetHeight) {
					dimension.pageWidth = document.body.scrollWidth;
					dimension.pageHeight = document.body.scrollHeight;
				} 
				else {
					dimension.pageWidth = document.body.offsetWidth;
					dimension.pageHeight = document.body.offsetHeight;
				}
			}

			if (window.innerHeight) {
				dimension.windowWidth = window.innerWidth;
				dimension.windowHeight = window.innerHeight;
			} 
			else if (document.documentElement && document.documentElement.clientHeight) {
				dimension.windowWidth = document.documentElement.clientWidth;
				dimension.windowHeight = document.documentElement.clientHeight;
			} 
			else if (document.body) {
				dimension.windowWidth = document.body.clientWidth;
				dimension.windowHeight = document.body.clientHeight;
			}         

			if (dimension.pageWidth < dimension.windowWidth) dimension.pageWidth = dimension.windowWidth;
			if (dimension.pageHeight < dimension.windowHeight) dimension.pageHeight = dimension.windowHeight;

			return dimension;
		},

		getImageSize: function (img){
			var ret = {w: img.width, h: img.height};
			return ret;
		},

		showDocumentProperties: function() {
			var obj = window.document.getElementById( 'document_info' );
			var page= ( bean.get( 'currentPage' ) !== 'undefined' ?  bean.get( 'currentPage' ) : 'undefined' );
			var maxPage= ( bean.get( 'pageCount' ) !== 'undefined' ?  bean.get( 'pageCount' ) : 'undefined' );
			if( maxPage.match(/undefined/) == null ) 
				txt = eval(page+1) + " of " + maxPage;
			else 
				txt = "n/a";
			obj.innerHTML = txt;
		}
	};
}());
//>>>

// Zoomer class: constructor must be called before the functionality
// of the magnifier can be used. The instance of the Zoomer class is a
// member of the webViewer object. Zoomer is automatically constructed
// in the "show" method of the WebViewer object.
opentext.Zoomer = function( img, opts ) {
//<<<
	var me = this;
	me.conf = {
// Adding the loading message for the initial image already
//		loadingStart: opentext.tools.loadingStart,
		loadingFail: opentext.tools.loadingFailed,
		loadingComplete: opentext.tools.loadingComplete,
		wall: 'opentext_viewer_canvas',
		lensRadius: 100,
		xOff: -10,
		yOff: -30,
		fadeLens: false,
		zoomFactor: 2
	};
	
	opentext.tools.merge(me.conf, opts);
	opentext.beans.webviewer.set( me.conf );

	me.canv = document.createElement('canvas');
	if(!me.canv) {
		console.error( 'ERROR: cannot create HTML5 element <CANVAS>. Does you browser support HTML5?' );
		opentext.tools.notification( opentext.localization.MSGID004, 'loading loading_red', 4000 );
		return null;
	}
	me.hcanv = document.createElement('canvas');
	
	me.canv.setAttribute( 'id', 'zoomer' );
	me.canv.style.position = 'absolute';
	me.canv.style.zIndex = 10000;
	me.canv.width = me.canv.height = me.hcanv.width = me.hcanv.height = me.conf.lensRadius*2;

//	document.body.appendChild(me.canv);
	if( me.conf.wall ) {
		var container = window.document.getElementById(me.conf.wall);
		container.appendChild(me.canv);
	} else {
		throw( { name: "opentext.Zoomer.constructor", message: opentext.localization.MSGID005 } );
	}
	
	if(typeof img === 'string')
		img = window.document.getElementById( img );
	
	me.image = img;
	me.doZomm = false;
//	me.zoom(opentext.tools.getZoom());
	
	listenOn(me.image, 'mousedown', me.mouseDown.scope(me));
//	listenOn(me.image, 'mousemove', me.mouseMove.scope(me));
//	listenOn(me.image, 'mouseup', me.mouseUp.scope(me));
	
	listenOn(me.canv,  'mousemove', me.mouseMove.scope(me));
	listenOn(me.canv,  'mouseup', me.mouseUp.scope(me));
//	listenOn(countainer, 'mouseup', me.mouseUp.scope(me));
	
	// TODO: touchscreen support
	listenOn( document.body, 'touchmove', function(event) { event.preventDefault(); } );
//	listenOn(me.image, 'touchmove', me.touchMove.scope(me));
	listenOn( me.image, 'touchdown', me.mouseDown.scope(me));
	listenOn( me.canvas, 'touchmove', me.mouseDown.scope(me));
	return me;
};
//>>>

opentext.Zoomer.prototype = {
//<<<
	zoom: function(z){
		console.log( 'opentext.Zoomer.zoom(' + z + ') entered' );
		var me = this;
		me.imgCanv = null;

		if(me.bigImage){
			if( me.bigImage.width == 0 ) 
				throw( { name: "opentext.Zoomer.zoom", message: opentext.localization.MSGID006 } );
			me.conf.zoom = z;
			me.imgCanv = opentext.tools.createScaledImageCanvas(me.image, me.conf.zoom, me.bigImage);
			console.log( 'opentext.Zoomer.zoom: magnifier input data:' +  me.imgCanv.width + '-' + me.imgCanv.height );
			return ;
		}

// Adding the loading message for the initial image already
//		if(me.conf.loadingStart)
//			me.conf.loadingStart(me);

//		opentext.tools.loadImage(me.image.title, function(im){
//		opentext.tools.loadImage(me.image.getAttribute('zoomer'), function(im){
//		TODO: custom made attribute 'zoomer' stopped to work in Chrome update 14....
		opentext.tools.loadImage(me.image.getAttribute('longDesc'), function(im){
				console.log( 'opentext.Zoomer.loadImage CallBack handler enter' );
				try {
					if(!im.complete) {
						if(me.conf.loadingFail)
							me.conf.loadingFail(me);
						return;
					}
					me.conf.zoom = z;
					me.bigImage = im;
					if(me.conf.loadingComplete)
						me.conf.loadingComplete(me);
					if( me.bigImage.width == 0 )
						throw( { name: "opentext.Zoomer.zoom", message: opentext.localization.MSGID007 } );
					me.imgCanv = opentext.tools.createScaledImageCanvas(me.image, me.conf.zoom, me.bigImage);
					console.log( 'opentext.Zoomer.zoom - CallBack handler: main image data:' +  me.image.width + '-' + me.image.height );
					console.log( 'opentext.Zoomer.zoom - CallBack handler: magnifier input data adjusted for magnifying ratio:' +  me.bigImage.width + '-' + me.bigImage.height );
					console.log( 'opentext.Zoomer.zoom - CallBack handler: magnifier output data adjusted for magnifying ratio:' +  me.imgCanv.width + '-' + me.imgCanv.height );
				} 
				catch(e) {
					console.error( "Exception: " + e.name + " - " + e.message );
					opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
					throw( { name: "opentext.Zoomer.zoom", message: e.message } );
				}
				console.log( 'opentext.Zoomer.loadImage CallBack handler exit' );
			});
		console.log( 'opentext.Zoomer.zoom exited' );
	},

	cursorWithinBounds: function(e){
		var me = this;
		var pos = me.getPagePossition(e);
//		var posX = pos.x;
//		var posY = pos.y;
		var minX = me.image.offsetLeft;
		var minY = me.image.offsetTop;
		var maxX = minX + me.image.clientWidth;
		var maxY = minY + me.image.clientHeight;
		
		return pos.x>minX && pos.y>minY && pos.x<maxX && pos.y<maxY;
	},
	
	setFade: function(m){
		this.conf.fadeLens = m;
	},
	
	getPagePossition: function(e) {
		// IE case
		var isIE = /*@cc_on!@*/false;
//		console.log( 'opentext.Zoomer.getPagePossition - is IE?: ' + isIE );

		if( isIE || (e.pageX == null) ) {
			var d =document.documentElement;
			if(!d)
				d= document.body;
			else if (!document.documentElement.scrollLeft)
				d= document.body;
			
			docX= e.clientX + d.scrollLeft;
			docY= e.clientY + d.scrollTop;
		} else {
		// all other browsers
			docX= e.pageX;
			docY= e.pageY;
		}
		return { x: docX, y: docY };
	},
	
	mouseDown: function(e) {
		var me = this;
		if(!me.imgCanv) return;
		me.canv.style.display = '';
		me.doZoom = true;
		me.mouseMove(e);
		if(e.preventDefault)
			e.preventDefault();
		else
			e.returnValue = false;
	},
	
	mouseUp: function(e){
		var me = this;
		me.canv.style.display = 'none';
		me.doZoom = false;
	},
	
	mouseMove: function(e){
		var me = this;
		if(!me.doZoom || !me.imgCanv)
			return;
		if(!me.cursorWithinBounds(e)){
			me.mouseUp(e);
			return;
		}
		var pos = me.getPagePossition(e);
//		var posX = pos.x;
//		var posY = pos.y;

		var centerX = me.canv.width/2;
		var centerY = me.canv.height/2;
		var clW = me.canv.width/2;
		var clH = me.canv.height/2;
		var ctx = me.canv.getContext('2d');
		var hctx = me.hcanv.getContext('2d');
		
//		console.log( "mouseMove: canvas position:" + clW + " - " + clH );
//		console.log( "mouseMove: X:" + pos.x + " Y:" + pos.y );
		me.canv.style.left =  (pos.x - clW + me.conf.xOff) + "px";
		me.canv.style.top =  (pos.y - clH + me.conf.yOff) + "px";

//		console.log( "mouseMove: left canvas position after update = " + me.canv.style.left );
//		console.log( "mouseMove: top canvas position after update = " + me.canv.style.top );
		
		ctx.globalCompositeOperation = 'source-over';
		var lf = pos.x - me.image.offsetLeft;
		var tp = pos.y - me.image.offsetTop;
		
//		console.log( "mouseMove: lf = " + lf );
//		console.log( "mouseMove: tp = " + tp );
		
		//Make xored shape due to chrome
		hctx.globalCompositeOperation = 'source-over';
		
//		hctx.fillRect(-1, -1, me.canv.width+1, me.canv.height+1);
		hctx.fillRect(0, 0, me.canv.width, me.canv.height);
		hctx.globalCompositeOperation = 'xor';
		hctx.beginPath();
		hctx.arc(centerX, centerY, clW, 0, Math.PI*2, true); 
		hctx.closePath();
		hctx.fill();
		
		lf = (lf + me.conf.xOff)*me.conf.zoom - clW;
		tp = (tp + me.conf.yOff)*me.conf.zoom - clH;

//		console.log( "mouseMove: canvas source image left = " + lf );
//		console.log( "mouseMove: canvas source image top = " + tp );
		
		if(lf<0) lf = 0;
		else if(lf>me.imgCanv.width - me.canv.width) lf = me.imgCanv.width - me.canv.width;
		if(tp<0) tp = 0;
		else if(tp>me.imgCanv.height - me.canv.height) tp = me.imgCanv.height - me.canv.height;
		
		ctx.drawImage(me.imgCanv, lf, tp, me.canv.width, me.canv.height, 0, 0, me.canv.width, me.canv.height);
		ctx.globalCompositeOperation = 'destination-out';
// MAME: comment this to obtain box ... add it to obtail circle
		ctx.drawImage(me.hcanv, 0, 0, me.canv.width, me.canv.height);
		
		ctx.globalCompositeOperation = 'source-over';
		ctx.strokeStyle="#414141";
		ctx.lineWidth=3;
		ctx.beginPath();
		ctx.arc(centerX, centerY,(clW - 2),0, (Math.PI/180)*360, false);
//		ctx.arc(140,140,138,0, (Math.PI/180)*360, false);
		ctx.stroke();
		ctx.closePath();
		
		if(me.conf.fadeLens){
			ctx.globalCompositeOperation = 'destination-atop';
			var gradObj = ctx.createRadialGradient(centerX,centerY,0, centerX, centerY,clW);
			gradObj.addColorStop(0.5, "rgba(0,0,0,1)");
			gradObj.addColorStop(1, "rgba(0,0,0,0.1)");
			ctx.fillStyle = gradObj;
			ctx.beginPath();
			ctx.arc(centerX, centerY, clW, 0, Math.PI*2, true); 
			ctx.closePath();
			ctx.fill();	
		}
	},

	// TODO: touchscreen support
	touchMove: function(e) {
		console.log("Touch event registered");
		alert('touchmove');
	}
};
//>>>

// WebViewer Constructor.
opentext.WebViewer = function(opts){
//		  <<<
	var me = this;
	var tools = opentext.tools;
	var dm = opentext.tools.getBrowserDimension();
	var UI = {
//<<<
			"elementCount": "8",
			"buttons": [
						{
							"strClass": 'toolbar-item toolbar-item-hover',
							"src": 'img/rback.png',
							"alt": "Previous page",
							"width": "64",
							"height": "64", 
							"onclick": 'opentext.WebViewer.UI.changePage( \"prev\" )'
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rforward.png',
							'alt': 'Next Page' ,
							'width': "64",
							'height':  "64",
							'onclick':  'opentext.WebViewer.UI.changePage( \"next\" )'
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rsend.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.send()"
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rsaveas.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.saveTo()"
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/ranno.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.displayAnnotation()"
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rzoom.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.changeZoom()"
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rabout.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.about()"
						},
						{
							'strClass':  'toolbar-item toolbar-item-hover',
							'src': 'img/rclose.png',
							'width': "64",
							'height':  "64",
							'onclick':  "opentext.WebViewer.UI.quit()"
						},
		]
	};
//>>>

	me.data = {
		cfg : {
			"currentPage": 0,
			"imageWidth": 0,
			"useZoomer": false,
			"wall": 'opentext_viewer_canvas'
				},
		UI : UI
	};

	try {
		opentext.tools.merge(me.data.cfg, opts);

		if( parseInt(me.data.cfg.imageWidth, 10 ) === 0 ) {
			if( me.data.cfg.useZoomer === true ) { 
				if( dm.pageWidth > 639 ) {
					me.data.cfg.imageWidth = 567;
				} else {
					me.data.cfg.imageWidth = (dm.pageWidth - 50);
				}
			} else {
				me.data.cfg.imageWidth = 1024;
			}
		}

		if( typeof me.data.cfg.al_url !== 'undefined' ) {
			me.data.cfg.aid = tools.getVal( me.data.cfg.al_url, 'contRep' );
			me.data.cfg.docId = tools.getVal( me.data.cfg.al_url, 'docId' );
			me.data.cfg.al_url = tools.encodeurl( me.data.cfg.al_url );
			me.data.cfg.showAnno = false;
		} else {
			throw( { name: 'opentext.WebViewer.constructor', message: opentext.localization.MSGID008 } );
		}

		if( me.data.cfg.viewerContainerID !== 'undefined' ) {
			me.data.cfg.viewerContainer = window.document.getElementById( me.data.cfg.viewerContainerID ); 
			if( me.data.cfg.viewerContainer == null )
				throw( { name: 'opentext.WebViewer.constructor', message: opentext.localization.MSGID009 } );
		} else {
			throw( { name: 'opentext.WebViewer.constructor', message: opentext.localization.MSGID009 } );
		}

		opentext.beans.webviewer.set( me.data.cfg );

	} catch( e ) {
			alert( e.name + ": " + e.message );
			throw( { name: 'opentext.WebViewer.constructor', message: e.message } );
	}

	if( opts.localization_test == true ) alert( opentext.localization.MSGID000 );
};
//>>>

// Class definition for the object WebViewer. The constructor must be
// called before the WebViewer object can be used. The constructor
// takes parameters that control the view.
//
// The constructor is called as a part of this library and global
// variable ww is exposed to calling HTML markup.
opentext.WebViewer.prototype = {
//<<<
		processData: function() {
//<<<
			var me = this;
			var bean = opentext.beans.webviewer;

			console.log( 'opentext.WebViewer.processData() enter' );


			me.data.cfg.mainImage = 'http://' + bean.get('webviewer') + '/WebViewer/faces/UI/component/rendering/m.jsp?url=';
/*
			me.data.cfg.mainImage = 'http://' + bean.get('webviewer') + '/WebViewer/faces/UI/component/rendering/m.jsp?xml=';
*/
			me.data.cfg.mainImage += bean.get('al_url');
			me.data.cfg.mainImage += '&quality=0.85&doc_id=';
			me.data.cfg.mainImage += bean.get('aid') + '_' + bean.get('docId');
			me.data.cfg.mainImage += '&showAnno=';
			me.data.cfg.mainImage += bean.get('showAnno');
			me.data.cfg.mainImage += '&user=';
			me.data.cfg.mainImage += bean.get('user');

			me.data.cfg.zoomData = me.data.cfg.mainImage;

			me.data.cfg.mainImage += '&width=' + bean.get('imageWidth') + '&index=' + bean.get('currentPage');

			me.data.cfg.zoomData += '&index=';
			me.data.cfg.zoomData += bean.get('currentPage');

			me.data.cfg.info_url= 'http://' + bean.get('webviewer') + '/WebViewer/faces/UI/component/rendering/m.json.jsp?url=';
/*
			me.data.cfg.info_url= 'http://' + bean.get('webviewer') + '/WebViewer/faces/UI/component/rendering/m.json.jsp?xml=';
*/
			me.data.cfg.info_url += bean.get('al_url');
			me.data.cfg.info_url += '&doc_id=';
			me.data.cfg.info_url += bean.get('aid') + '_' + bean.get('docId') + '&';
			me.data.cfg.info_url += 'showAnno=';
			me.data.cfg.info_url += bean.get('showAnno');
			me.data.cfg.info_url += '&user=';
			me.data.cfg.info_url += bean.get('user');

			bean.set( me.data.cfg );

			console.log( 'opentext.WebViewer.processData() created URL = ' + me.data.cfg.mainImage );
			console.log( 'opentext.WebViewer.processData() exit' );
		},
//>>>

		createView: function() {
			//<<<
			var me = this;
			var bean = opentext.beans.webviewer;
			var dm = opentext.tools.getBrowserDimension();
			me.viewerContainer = bean.get( 'viewerContainer' );

			console.log('opentext.WebViewer.createView() enter');
			var wall = ( bean.get( 'wall' ) !== 'undefined' ? bean.get( 'wall' ) : 'opentext_viewer_canvas' );
			var hasBackground = ( bean.get( 'hasBackground' ) !== 'undefined' ? bean.get( 'hasBackground' ) : 'false' );
			var uiMask = ( bean.get( 'UIMask' ) !== 'undefined' ? bean.get( 'UIMask' ) : { "back": true, "forward": true, "send": true, "save": true, "anno": true, "zoom": true, "about": true, "quit": true} );

			// register the callback object hoisting the methods called
			// back from the UI event handler.
			opentext.WebViewer.UI.setCbObj( me );

			if( hasBackground ) {
				me.background = window.document.createElement('div');
				me.background.setAttribute('class', 'viewer_background');
				me.background.setAttribute('id', 'viewer_background');

				me.background.style.width = dm.pageWidth + 'px';
				me.background.style.height = dm.pageHeight + 'px';
			}

			me.img_canvas = window.document.createElement('div');
			me.img_canvas.setAttribute('class', 'viewer_canvas' );
			me.img_canvas.setAttribute('id', wall );

			me.toolbar = document.createElement('div');
			me.toolbar.setAttribute('id', 'viewer_toolbar');
			me.toolbar.setAttribute('class', 'toolbar' );
			me.toolbar.buttons = new Array(parseInt(me.data.UI.elementCount,10));

			for ( var i in me.data.UI.buttons) {
				me.toolbar.buttons[i] = document.createElement('img');
				me.toolbar.buttons[i].setAttribute( 'class',  me.data.UI.buttons[i].strClass );
				me.toolbar.buttons[i].setAttribute( 'src', 'http://' + bean.get('webviewer') + '/WebViewer/res/' + me.data.UI.buttons[i].src );
				me.toolbar.buttons[i].setAttribute( 'alt', me.data.UI.buttons[i].alt );
				me.toolbar.buttons[i].setAttribute( 'width', me.data.UI.buttons[i].width );
				me.toolbar.buttons[i].setAttribute( 'height',  me.data.UI.buttons[i].height );
				me.toolbar.buttons[i].setAttribute( 'onclick',  me.data.UI.buttons[i].onclick );
			}

			me.toolbar.info = document.createElement('div');
			me.toolbar.info.setAttribute('id', 'document_info' );
			me.toolbar.info.setAttribute( 'class',  'toolbar-item toolbar-item-info-box toolbar-item-hover');
			me.toolbar.info.innerHTML = " n/a ";

			if( uiMask.back )
				me.toolbar.appendChild(me.toolbar.buttons[0]);
			if( uiMask.forward ) 
				me.toolbar.appendChild(me.toolbar.buttons[1]);
			if( uiMask.send ) 
				me.toolbar.appendChild(me.toolbar.buttons[2]);
			if( uiMask.save ) 
				me.toolbar.appendChild(me.toolbar.buttons[3]);
			if( uiMask.anno ) 
				me.toolbar.appendChild(me.toolbar.buttons[4]);
			if( uiMask.zoom ) 
				me.toolbar.appendChild(me.toolbar.buttons[5]);
			if( uiMask.about ) 
				me.toolbar.appendChild(me.toolbar.buttons[6]);

			me.toolbar.appendChild(me.toolbar.info );

			if( uiMask.quit ) 
				me.toolbar.appendChild(me.toolbar.buttons[7]);

			/* Start of JQuery
			if( hasBackground )
				document.body.insertBefore( me.background, document.body.firstChild );
			document.body.appendChild(me.img_canvas);
			document.body.appendChild(me.toolbar);
			*/
			me.viewerContainer.appendChild(me.img_canvas);
			me.viewerContainer.appendChild(me.toolbar);
			if( hasBackground )
				me.viewerContainer.insertBefore( me.background, me.viewerContainer.firstChild );
			
			// END of JQuery

			console.log('opentext.WebViewer.createView() exit');
		},
//>>>

		createPage: function() {
//<<<
//			var txt;
			var me = this;
			var stopwatch = new Date();
			var bean = opentext.beans.webviewer;

			console.log('opentext.WebViewer.createPage() enter');

			try {
				if( typeof me.img_canvas !== 'undefined' ) {				
					me.img = window.document.createElement('img');
					me.img.setAttribute('id', 'image');
					me.img.setAttribute( 'src', me.data.cfg.mainImage);
					me.img.setAttribute( 'alt', 'Page generated by WebViewer 10.3.0' );
					me.img.setAttribute('onerror','errorPage("createError")');
//				me.img.setAttribute( 'zoomer', me.data.cfg.zoomData);
//				Custom attribute 'zoomer' stopped to work in Chrome 14....
//				TODO
//				This pattern does not seem to work with new, fully assynchrnous browsers. There
//				is always a delay whn Image loads and application is not ready for it. It simply
//				runs forward without having the image loaded. It fails then
					me.img.setAttribute( 'longDesc', me.data.cfg.zoomData);
					me.img_canvas.appendChild(me.img);
					console.log( 'TRACE: createPage - markup img just added to the page at ' + stopwatch.getTime());

					// TODO - START Zoomer neni potreba
					if( bean.get( 'useZoomer' ) === true )
						this.data.zoomer = new opentext.Zoomer('image', me.data.cfg );

					listenOn( me.img, "load", me.zoom.scope(me, (typeof bean.get( 'zoomFactor' ) === 'integer' ?  bean.get( 'zoomFactor' ) : 2 )));
					// TODO END

// Add image loading right in the beginning
					opentext.tools.loadingStart();
//					this.zoom((typeof opentext.beans.webviewer.get( 'zoomFactor' ) === 'integer' ?  opentext.beans.webviewer.get( 'zoomFactor' ) : 2 ));
				} else {
					console.error( 'opentext.WebViewer.createPage() failed: cannot add image to non-existent canvas' );
					throw( { name: 'opentext.WebViewer.createPage()', message: opentext.localization.MSGID010 } );
				}
			} catch(e) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
			console.log('opentext.WebViewer.createPage() exit');
		},
//>>>

		createAnno: function() {
//<<<
			var o = {};
			var me = this;
			var bean = opentext.beans.webviewer;

//			me.jg = new jsGraphics( document.getElementById( bean.get( 'wall' )));
			o.show_annotation = 0;
			bean.set( o );
		},
//>>>

		destroyView: function() {
//<<<
			var me = this;
			var bean = opentext.beans.webviewer;

			try {
				console.log('opentext.WebViewer.destroyView() called' );
				var hasBackground = ( bean.get( 'hasBackground' ) !== 'undefined' ? bean.get( 'hasBackground' ) : 'false' );
				var callbackOnExit = ( bean.get( 'callbackOnExit' ) !== 'undefined' ? bean.get( 'callbackOnExit' ) : 'false' );

				if(( typeof me.toolbar !== 'undefined') ) {
					//				document.body.removeChild( me.toolbar );
					me.viewerContainer.removeChild( me.toolbar );
					delete me.toolbar;
				} else {
					console.error( 'opentext.WebViewer.destroyView() failed: cannot find the image toolbar' );
				}

				if(( typeof me.img_canvas !== 'undefined') ) {
					//				document.body.removeChild( me.img_canvas );
					me.viewerContainer.removeChild( me.img_canvas );
					delete me.img_canvas;
				} else {
					console.error( 'opentext.WebViewer.destroyView() failed: cannot find the image img_canvas' );
				}

				if( hasBackground ) {
					if(( typeof me.background !== 'undefined') ) {
						//					document.body.removeChild( me.background );
						me.viewerContainer.removeChild( me.background );
						delete me.background;
					} else {
						console.error( 'opentext.WebViewer.destroyView() failed: cannot find the image backbground' );
					}
				}

				opentext.beans.webviewer.set( { pageCount: 'undefined' } );

				if( callbackOnExit !== false && typeof callbackOnExit == 'string' )
					setTimeout( callbackOnExit, 300 ); 

			} catch( e ) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
		},
//>>>

		destroyPage: function() {
//<<<
			var me = this;
	
			try {
				console.log('opentext.WebViewer.destroyPage() called' );
				if( typeof me.img_canvas !== 'undefined' )  {
					if( typeof me.data.zoomer !== 'undefined' ) {
						var c = window.document.getElementById( 'zoomer' );
						( typeof c !== 'undefined' ) && me.img_canvas.removeChild( c );
						delete me.data.zoomer;
					}
					if( typeof me.img !== 'undefined' ) {
						me.img_canvas.removeChild( me.img );
						delete me.img;
					}
				} else {
					console.error( 'opentext.WebViewer.destroyPage() failed: cannot find the image element' );
					throw( { name: 'opentext.WebViewer.destroyPage()', message: opentext.localization.MSGID010 } );
				}
			} catch( e ) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
		},
//>>>

		show: function() {
//<<<
			try {
				console.log('opentext.WebViewer.show() enter');
				this.processData();
				this.createView();
				this.createPage();
				this.createAnno();
				console.log('opentext.WebViewer.show() exit');
			} catch(e) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
		},
//>>>
	
		init: function( cnvId ) {
//<<<
			console.log("opentext.WebViewer.init()");
			var canvas = document.getElementById( cnvId );
				
			// Check the element is in the DOM and the browser supports canvas
			if(!(canvas && canvas.getContext)) {
      		return;
   		}
      
			// Initaliase a 2-dimensional drawing context
			var context = canvas.getContext('2d');

   		opentext.tools.data.pencil = new opentext.FreehandTool( canvas, context );
//   		opentext.tools.data.pencil = new FreehandTool( canvas, context );
	
			// Attach the mousedown, mousemove and mouseup event listeners.
   		// I am attaching the event to the canvas itself, which is not changed
   		// by transformation at all. So the offsetX/Y are comming always
   		// in correct coordinate system.
			// canvas.addEventListener('mousedown', ev_canvas, false);
			// canvas.addEventListener('mousemove', ev_canvas, false);
			// canvas.addEventListener('mouseup',   ev_canvas, false);
			listenOn( canvas, 'mousedown', ev_canvas.scope, false );
			listenOn( canvas, 'mousemove', ev_canvas.scope, false );
			listenOn( canvas, 'mouseup', ev_canvas.scope, false );
		},	
// >>>
		
		zoom: function( zf ) {
//<<<
			var bean = opentext.beans.webviewer;

			try {
				if( bean.get('useZoomer') === true ) { 
					console.log('opentext.WebViewer.zoom(' + zf + ') enter');
					this.data.zoomer.zoom((typeof zf !== 'undefined' ?  zf : 2 ));
					console.log('opentext.WebViewer.zoom(' + zf + ') exit');
				} else {
					console.log('opentext.WebViewer.zoom(' + zf + ') skipped');
					opentext.tools.loadingComplete( this );
				}
			} catch( e ) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
		},
//>>>

		drawAnnotation: function() {
//<<<
			var me = this;
			var bean = opentext.beans.webviewer;
			var o = {};

			/* 
				This is dead code. It can be eventually used if the wz_jsGraphics.js drawing library 
				is deployed.
			*/

			return;


		},
//>>>

		showAbout: function() {
						  //<<<
			var me = this;
			var bean = opentext.beans.webviewer;

			try {
				if( typeof me.img_canvas !== 'undefined' ) {
					if( typeof me.aboutBoxEnvelop == 'undefined' ) {				
						me.aboutBoxEnvelop = window.document.createElement('div');
						me.aboutBoxEnvelop.setAttribute('id', 'stage');

						me.aboutBoxInner = window.document.createElement('div');
						me.aboutBoxInner.setAttribute( 'id', 'spinner' );
						me.aboutBoxInner.innerHTML = '<table border=\"0\"><tr><td><img src=\"http://' + bean.get('webviewer') + '/WebViewer/res/img/chicklet.png\"</td><td>&nbsp;</td><td>HTML5 WebViewer Library<br/>Martin Metal, 2011<br/>OpenText Corporation</td></tr></table>';
						me.aboutBoxEnvelop.appendChild(me.aboutBoxInner);
						me.img_canvas.appendChild(me.aboutBoxEnvelop);
					} else {
						me.img_canvas.removeChild( me.aboutBoxEnvelop );
						delete me.aboutBoxEnvelop;
					}
				} else {
					console.error( "Cannot find the parent HTML element to include the about box into. Bailing out." );
					throw({ name: 'opentext.WebViewer.showAbout()', message: opentext.localization.MSGID012 });
				}
			} catch( e ) {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
			}
		},
		//>>>

		saveTo: function() {
		// <<<
			var me = this;
			var bean = opentext.beans.webviewer;

			var callback = ( bean.get( 'callbackOnSaveTo' ) !== 'undefined' ? bean.get( 'callbackOnSaveTo' ) : 'false' );

			if( callback !== false && typeof callback == 'string' ) {
				setTimeout( callback , 300 ); 
				console.log( "Called the callback registered for the saveto fucntion");
			} else {
				console.warn( "Cannot call the callback method for function saveto. Is probably not registered." );
			}
		}
		// >>>

};
//>>>

// Object literal opentext.WebViewer.UI is the container for the event
// handlers. The object is static, fully defined in runtime.  Does not
// need to call constructor.
//
// The object provides the container for callback routine pointers,
// that can point to the any object in the namespace opentext. This
// strategy allows to define handlers statically and thus accessible
// to the dynamically generated markup.
//
// call first setCB( object ) if you want to register a specific
// objects before you use the handler. The shortcomings of the pattern
// is that you can call  back methods localed in one object only!
opentext.WebViewer.UI = {
//<<<
	callback: {}, 

	setCbObj: function( o ) {
		this.callback = o;
	},

	send: function() {
		console.log( 'opentext.WebViewer.UI.send handler called' );
		if( typeof this.callback.hide === 'function' )
			this.callback.hide(); 
		else
			console.log( 'reqested operation is not the method on the registered object' );
	},

	changeZoom: function() {
		try {
			console.log( 'opentext.WebViewer.UI.changeZoom() handler called' );
			var o = {};
			var zoomFactor = (typeof opentext.beans.webviewer.get( 'zoomFactor' ) !== 'undefined' ? opentext.beans.webviewer.get( 'zoomFactor' ) : 2 );
			zoomFactor += 0.5;
			zoomFactor = ( (zoomFactor > 4) ? 2 : zoomFactor );

			o.zoomFactor = zoomFactor;
			opentext.beans.webviewer.set( o );
			opentext.tools.infoZoomFactorShow( zoomFactor );
			this.callback.zoom(opentext.beans.webviewer.get( 'zoomFactor' ));
		} catch( e ) {
			opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
		}
	},

	changePage: function( dir ) {
		try {
			console.log( 'opentext.WebViewer.UI.nextPage(' + dir + ') handler called' );
			var page= (opentext.beans.webviewer.get( 'currentPage' ) !== 'undefined' ? opentext.beans.webviewer.get( 'currentPage' ) : 0 );
			var maxPage= (opentext.beans.webviewer.get( 'pageCount' ) !== 'undefined' ? opentext.beans.webviewer.get( 'pageCount' ) : 1e8 );

			console.log( 'opentext.WebViewer.UI.nextPage() retrieved page number: ' + page  );

			if(( typeof dir === "string" && dir === "next" ) && ( (page + 1) < maxPage )) {
				page += 1;
				this.doChangePage( page );
			} 
			else if ((typeof dir === "string") && (dir === "prev") && (page > 0)) {
				page -= 1;
				this.doChangePage( page );
			} else {
				opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Information</b>: <br/>&nbsp;&nbsp;&nbsp;" + opentext.localization.MSGID011, 'loading loading_blue', 2500 );
				console.warn( "Believe me, this document does not have more pages" );
			}
		} catch( e ) {
			opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
		}
	},

	doChangePage: function( page ) {
		var o = {};
		o.currentPage = page;
		opentext.beans.webviewer.set( o );
		opentext.tools.merge( this.callback.data.cfg, opentext.beans.webviewer.conf );
		this.callback.processData();
		this.callback.destroyPage();
		this.callback.createPage();
	},

	quit: function() {
		console.log( 'opentext.WebViewer.UI.quit() handler called' );
		this.callback.destroyPage();
		this.callback.destroyView();
		delete ww;
	},

	displayAnnotation_v1: function() {
		console.log( 'opentext.WebViewer.UI.displayAnnotation() handler called' );
		this.callback.drawAnnotation();
	},

	displayAnnotation: function() {
		try {
			console.log( 'opentext.WebViewer.UI.displayAnnotation() handler called' );
			var o = {};
			var anno = ( opentext.beans.webviewer.get( 'showAnno' ) !== 'undefined' ? opentext.beans.webviewer.get( 'showAnno' ) : false );
			var page = ( opentext.beans.webviewer.get( 'currentPage' ) !== 'undefined' ? opentext.beans.webviewer.get( 'currentPage' ) : 0 );
			o.showAnno = !(anno==true);
			opentext.beans.webviewer.set( o );
			this.doChangePage( page );
		} catch( e ) {
			opentext.tools.notification( "&nbsp;&nbsp;&nbsp;<b>Error</b>: " + e.name + '<br/>&nbsp;&nbsp;&nbsp;<b>Details</b>: ' + e.message, 'loading loading_red', 9000 );
		}
	},

	saveTo: function() {
		console.log( 'opentext.WebViewer.UI.saveTo() handler called' );

		this.callback.saveTo();

	},

	about: function() {
		console.log( 'opentext.WebViewer.UI.about() handler called' );

		this.callback.showAbout();

	}

};
//>>>

function errorPage(msg)
{              
	if(msg=="destroy")
    {
		ww.destroyPage();
        ww.destroyView();
     }
     else
     {
		 var mCloseButton='<div align="center"><br/><input type="button" value="'+opentext.localization.MSGID015+'" onclick="errorPage(&#34;destroy&#34;)" /></div>';
		 opentext.tools.notification("<b>"+opentext.localization.MSGID013+"</b><br/>"+opentext.localization.MSGID014+mCloseButton,'loading loading_gray', 300000 );
		 //opentext.tools.notification( "&nbsp;<b>" + opentext.localization.MSGID013+"</b>", 'loading loading_red', 3000 );
		 // setTimeout('errorPage("destroy")',3000);
	 }
}
